package com.abc.core.tool;

import com.abc.core.consts.CodeDefConst;
import com.abc.core.consts.DataType;
import com.abc.core.consts.OperationFlag;
import com.abc.core.def.Dna;
import com.abc.core.def.Vd;
import com.abc.core.instance.bo.Cell;
import com.abc.core.instance.bo.FilterResultCell;
import com.abc.core.instance.bo.Inst;
import com.abc.core.instance.bo.Va;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class DnaTool {
    public static Cell dna2Cell(String instType, Dna dna) {
        Cell cell = singleDna2Cell(instType, dna);

        for (Dna childDna : dna.getChildren()) {
            Inst childInst = new Inst(instType, childDna, cell);
            cell.addChildInst(childDna.getDnaName(), childDna.getDnaCode(), childInst);

            for (int i = 0; i < childDna.getMinCount(); i++) {
                Cell childCell = dna2Cell(instType, childDna);
                childInst.addCell(childCell);
            }
        }

        if (dna.isCursive()) {
            Inst childInst = new Inst(instType, dna, cell);
            cell.addChildInst(dna.getDnaName(), dna.getDnaCode(), childInst);
        }

        return cell;
    }

    public static Cell singleDna2Cell(String instType, Dna dna) {
        if (dna == null) {
            return null;
        }

        Cell cell = null;

        if (instType.equals(CodeDefConst.INST_TYPE_DEFAULT)) {
            cell = new Cell();
        } else if (instType.equals(CodeDefConst.INST_TYPE_FILTER_RESULT)) {
            cell = new FilterResultCell(dna.getDnaCode());
        }

        cell.setOperationFlag(OperationFlag.OPERATION_FLAG_NEW);

        for (Vd vd : dna.getVds()) {
            Va va = vd2va(vd);
            cell.getVas().put(vd.getVdName(), va);
        }

        return cell;
    }

    private static Va vd2va(Vd vd) {
        if (vd == null) {
            throw new RuntimeException("属性定义vd不能为空");
        }

        Va va = new Va();
        va.setCode(vd.getVdCode());
        va.setName(vd.getVdName());
        String dataType = vd.getDataType();
        va.setDataType(dataType);
        if (dataType.equals(DataType.DATA_TYPE_INT)) {
            va.setValue(0);
        } else if (dataType.equals(DataType.DATA_TYPE_LONG)) {
            va.setValue(0L);
        } else if (dataType.equals(DataType.DATA_TYPE_FLOAT)) {
            va.setValue(0.0);
        }

        return va;
    }

    /**
     * 2.4.2 元数据实例与POJO基于参数转换
     * POJO 属性名与Dna中Vd的名相同
     * POJO 子对象名与Dna的 dnaName相同
     *
     * @param <T>
     * @param objs
     * @param dna
     * @return
     */
    public static <T> Inst object2Inst(List<T> objs, Dna dna) {
        if (objs == null) {
            return null;
        }

        Inst inst = new Inst(dna);
        for (Object obj : objs) {
            if (obj == null) {
                continue;
            }
            Cell cell = dna2Cell(CodeDefConst.INST_TYPE_DEFAULT, dna);
            inst.addCell(cell);

            for (Vd vd : dna.getVds()) {
                Va va = cell.getVaByName(vd.getVdName());
                objectField2Va(obj, vd.getVdName(), va);
            }

            for (Dna childDna : dna.getChildren()) {
                List childObjs = getListFieldOfObject(obj, childDna.getDnaName());
                if (childObjs != null) {
                    Inst childInst = object2Inst(childObjs, childDna);
                    cell.addChildInst(childInst);
                }
            }

            if (dna.isCursive()) {
                List childObjs = getListFieldOfObject(obj, dna.getDnaName());
                if (childObjs != null) {
                    Inst childInst = object2Inst(childObjs, dna);
                    cell.addChildInst(childInst);
                }
            }
        }
        return inst;
    }

    /**
     * 2.4.2 元数据实例与POJO基于参数转换
     * 更通用转换
     * 支持POJO属性名与Vd名不同，子Dna与POJO下子对象名称不同
     * 部分属性不参与转换
     *
     * @param <T>
     * @param objs
     * @param dna
     * @param vdNameMap
     * @param ignoreVdNames
     * @return
     */
    public static <T> Inst object2Inst(List<T> objs, Dna dna, Map<String, String> vdNameMap,
                                       Set<String> ignoreVdNames) {
        if (objs == null) {
            return null;
        }

        Inst inst = new Inst(dna);

        String vdName;
        for (Object obj : objs) {
            if (obj == null) {
                continue;
            }

            Cell cell = dna2Cell(CodeDefConst.INST_TYPE_DEFAULT, dna);
            inst.addCell(cell);

            for (Vd vd : dna.getVds()) {
                if (ignoreVdNames.contains(vd.getVdName())) {
                    continue;
                }
                Va va = cell.getVaByName(vd.getVdName());
                vdName = vdNameMap.get(vd.getVdName());
                if (vdName == null) {
                    vdName = vd.getVdName();
                }
                objectField2Va(obj, vdName, va);
            }

            for (Dna childDna : dna.getChildren()) {
                if (ignoreVdNames.contains(childDna.getDnaName())) {
                    continue;
                }

                vdName = vdNameMap.get(childDna.getDnaName());
                if (vdName == null) {
                    vdName = childDna.getDnaName();
                }

                List childObjs = getListFieldOfObject(obj, vdName);
                if (childObjs != null) {
                    Inst childInst = object2Inst(childObjs, childDna, vdNameMap, ignoreVdNames);
                    cell.addChildInst(childInst);
                }
            }

            if (dna.isCursive() && !ignoreVdNames.contains(dna.getDnaName())) {
                vdName = vdNameMap.get(dna.getDnaName());
                if (vdName == null) {
                    vdName = dna.getDnaName();
                }

                List childObjs = getListFieldOfObject(obj, vdName);
                if (childObjs != null) {
                    Inst childInst = object2Inst(childObjs, dna, vdNameMap, ignoreVdNames);
                    cell.addChildInst(childInst);
                }
            }
        }

        return inst;
    }

    private static void objectField2Va(Object obj, String vdName, Va va) {
        //TODO return null;
    }

    private static List getListFieldOfObject(Object obj, String vdName) {
        //TODO
        return null;
    }

    public static <T> List<T> inst2Object(Inst inst, Class<T> clazz) {
        List<T> resultObjects = new ArrayList<T>();
        for (Cell cell : inst.getCells()) {
            T obj = null;
            try {
                obj = clazz.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
                return null;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return null;
            }

            for (String dnaName : cell.getVas().keySet()) {
                Va va = cell.getVas().get(dnaName);
                va2ObjectField(va, obj, dnaName);
            }

            resultObjects.add(obj);

            for (String dnaName : cell.getChildren().keySet()) {
                try {
                    Field f = obj.getClass().getDeclaredField(dnaName);
                    // 递归调用inst2Object, 第二层参数clazz不完备
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (SecurityException e) {
                    e.printStackTrace();
                }
            }
        }

        return resultObjects;
    }

    public static <T> List<T> inst2Object(Inst inst, Class<T> clazz, Map<String, String> classNameMap,
                                          Map<String, String> vdNameMap) {
        List<T> resultObjects = new ArrayList<T>();

        Field[] fields = clazz.getDeclaredFields();
        for (Cell cell : inst.getCells()) {
            T obj = createObjectByClass(clazz);
            for (String vdName : cell.getVas().keySet()) {
                Va va = cell.getVas().get(vdName);
                String objectVdName = getObjectVdName(inst.getDnaName(), vdName, vdNameMap);
                if (hasFieldByName(fields, objectVdName)) {
                    va2ObjectField(va, obj, objectVdName);
                }
            }

            resultObjects.add(obj);

            for (String dnaName : cell.getChildren().keySet()) {
                Inst childInst = cell.getChildInst(dnaName);
                String className = classNameMap.get(dnaName);
                if (className == null) {
                    continue;
                }

                Class childClazz = null;

                try {
                    childClazz = Class.forName(className);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    throw new RuntimeException("找不到类名:" + className);
                }

                List childList = inst2Object(childInst, childClazz, classNameMap, vdNameMap);
                String objectVdName = getObjectVdName(inst.getDnaName(), dnaName, vdNameMap);
                if (hasFieldByName(fields, objectVdName)) {
                    list2ObjectField(childList, obj, objectVdName);
                }
            }
        }

        return resultObjects;
    }

    private static <T> void va2ObjectField(Va va, T obj, String objectVdName) {
        //TODO  return null;
    }

    private static <T> void list2ObjectField(List childList, T obj, String objectVdName) {
        //TODO return null;
    }

    private static String getObjectVdName(String dnaName, String dnaName1, Map<String, String> vdNameMap) {
        //TODO
        return null;
    }

    private static boolean hasFieldByName(Field[] fields, String objectVdName) {
        //TODO
        return false;
    }

    private static <T> T createObjectByClass(Class<T> clazz) {
        //TODO
        return null;
    }
}
